﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Web;


namespace Sharp.Infrastructure.Search
{
    public partial class QuerySupport<TEntity>
    {
        static BindingFlags flag = BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance;
        #region 过滤条件
        /// <summary>
        /// 数值型
        /// </summary>
        private static readonly Type[] NumberType = { typeof(int), typeof(decimal), typeof(float), typeof(double) };

        /// <summary>
        /// 参数表达式
        /// </summary>
        public ParameterExpression Parameter { get; set; }

        /// <summary>
        /// 创建查询条件
        /// </summary>
        /// <param name="pf"></param>
        /// <returns></returns>
        public Expression<Func<TEntity, bool>> TobuildFilter(PagingFeature pf)
        {
            if (pf.RequestString.IsNullOrEmpty())
            {
                return x => true;
            }
            if (pf.Optimizes == null)
            {
                pf.Optimizes = new List<ComparisonOptimize>();
            }
            List<SearchCondition> searchs = new List<SearchCondition>();

            var nameValue = HttpUtility.ParseQueryString(pf.RequestString);

            #region 自动创建
            for (int i = 0; i < nameValue.Keys.Count; i++)
            {
                string key = nameValue.Keys[i];
                string value = nameValue[i];
                if (value.IsNullOrEmpty() || value == "null" || Ignore(key))
                {
                    continue;
                }

                string fieldName = nameValue.Keys[i].Substring(0, 1).ToUpper() + nameValue.Keys[i].Substring(1);
                if (fieldName.IndexOf('_') > -1)
                    fieldName = fieldName.Substring(0, fieldName.IndexOf('_'));
                //fieldName = fieldName.TrimEnd('[', ']');
                fieldName = fieldName.Replace("[", "").Replace("]", "");

                //因为区间查询传递同一个字段两个值
                if (searchs.Any(x => x.For == fieldName))
                {
                    continue;
                }

                PropertyInfo property = FindProperty(fieldName, typeof(TEntity));

                if (property == null)
                {
                    continue;
                }
                if (nameValue.AllKeys.Any(k => k.Equals(fieldName + "_from", StringComparison.CurrentCultureIgnoreCase)))
                {
                    #region 起始框搜索
                    searchs.Add(new SearchCondition(fieldName, key,
                        ComparisonType.Section, property.PropertyType,
                        nameValue[fieldName + "_from"], nameValue[fieldName + "_to"]));

                    #endregion
                    continue;
                }

                if (key.IndexOf("[]") > -1)
                {
                    var searchCondition = new SearchCondition(fieldName, key);
                    searchCondition.Operation = ComparisonType.In;
                    searchCondition.Values = value.Split(',');
                    searchCondition.PropertyType = property.PropertyType;
                    searchs.Add(searchCondition);
                }
                else if (property.PropertyType == typeof(string))
                {
                    searchs.Add(new SearchCondition(fieldName, key, ComparisonType.Like, property.PropertyType, nameValue[fieldName]));
                }
                else if (nameValue.AllKeys.Contains(fieldName) && !nameValue[fieldName].Contains(",")
                ) //值类型直接等于，有些fieldName是范围查询加了后缀，不需要走进这个判断
                {
                    searchs.Add(new SearchCondition(fieldName, key, ComparisonType.Equal, property.PropertyType,
                        nameValue[fieldName]));
                }
                else
                {
                    throw new NotSupportedException("居然有没判断到的条件");
                }

            }

            #endregion

            #region 追加和覆盖条件
            pf.Filters.ForEach(item =>
            {
                //添加额外的搜索
                if (searchs.All(x => x.For != item.For))
                {
                    //PropertyType不是外部传递进来的，需要在这里给PropertyType赋值
                    item.PropertyType = FindProperty(item.For, typeof(TEntity)).PropertyType;

                    if (item.PropertyType != null)
                        searchs.Add(item);
                }

            });

            pf.Optimizes.ForEach(item =>
            {
                //覆盖一些条件
                var tagetItem = searchs.FirstOrDefault(s => s.For.Equals(item.Field, StringComparison.OrdinalIgnoreCase));
                if (tagetItem != null && tagetItem.Operation == item.OldComparisonType)
                {
                    tagetItem.Operation = item.OptimizeComparisonType;
                }
            });

            #endregion

            return ToFilter(searchs);
        }

        #region 生成筛选表达
        /// <summary>
        /// 生成筛选表达
        /// </summary>
        private Expression<Func<TEntity, bool>> ToFilter(List<SearchCondition> searchs)
        {
            if (searchs == null)
            {
                return x => true;
            }

            if (Parameter == null)
            {
                Parameter = Expression.Parameter(typeof(TEntity), "m");
            }

            Expression expression = Expression.Constant(true);


            foreach (var search in searchs)
            {
                if (search.Operation == ComparisonType.Section)
                {
                    #region 起始框搜索

                    var beginThan = GetSection(search.PropertyType, search.Values[0], true, search.For);
                    var endThan = GetSection(search.PropertyType, search.Values[1], false, search.For);

                    if (beginThan != null)
                        expression = Expression.And(beginThan, expression);
                    if (endThan != null)
                        expression = Expression.And(endThan, expression);

                    #endregion
                }
                else if (search.Operation == ComparisonType.In)
                {
                    //如果没有数据
                    if (search.Values.Length == 0)
                    {
                        continue;
                    }

                    var valueExpressions = new List<Expression>();
                    foreach (var value in search.Values)
                    {
                        Type propertyType = search.PropertyType;

                        Expression constant = Expression.Constant(ConvertHelper.ChangeType(value, propertyType));

                        valueExpressions.Add(constant);
                    }

                    expression = Expression.And(
                        GetORGateExpression(Parameter, valueExpressions, search.For, search.Operation),
                        expression);
                }
                else if (new[] { ComparisonType.Equal, ComparisonType.NotEqual }.Contains(search.Operation))
                {
                    Type proType;
                    if (!search.PropertyType.GenericTypeArguments.Any())
                    {
                        proType = search.PropertyType;
                    }
                    else
                    {
                        proType = search.PropertyType.GenericTypeArguments.First();
                    }
                    Expression constant = Expression.Constant(ConvertHelper.ChangeType(search.Value, proType));

                    Expression right;
                    if (search.Operation == ComparisonType.Equal)
                    {
                        right = Expression.Equal(
                            GetPropertyExpression(null, Parameter, search.For),
                            Expression.Convert(constant, search.PropertyType));
                    }
                    else if (search.Operation == ComparisonType.NotEqual)
                    {
                        right = Expression.NotEqual(
                            GetPropertyExpression(null, Parameter, search.For),
                            Expression.Convert(constant, search.PropertyType));
                    }
                    else
                    {
                        throw new NotSupportedException();
                    }

                    expression = Expression.And(right, expression);
                }
                else if (search.Operation == ComparisonType.Like)
                {
                    #region 输入框搜索

                    Expression right = Expression.Call(GetPropertyExpression(null, Parameter, search.For),
                        typeof(string).GetMethod("Contains"),
                        Expression.Constant(search.Value));

                    expression = Expression.And(right, expression);

                    #endregion
                }
                else
                {
                    throw new NotSupportedException("尚未支持" + search.Operation);
                }
            }


            var filter = Expression.Lambda<Func<TEntity, bool>>(expression, Parameter);

            return filter;
        }
        #endregion

        #endregion


        #region 表达式

        private Expression GetORGateExpression(ParameterExpression left, IEnumerable<Expression> options, string fieldName, ComparisonType comparisonType)
        {
            Expression expression = Expression.Constant(false);

            var firstProperty = FindProperty(fieldName.Split('.')[0], typeof(TEntity)).PropertyType;

            var lastProperty = FindProperty(fieldName, typeof(TEntity)).PropertyType;
            MethodInfo m;
            if (lastProperty.Name == "Nullable`1")
            {
                m = lastProperty.GetMethod("op_Equality");
            }
            else
            {
                m = lastProperty.GetMethod("Equal");
            }
            foreach (var constant in options)
            {
                Expression right;
                //if (firstProperty.GenericTypeArguments.Length == 0)
                //非集合类型
                if (firstProperty != typeof(string) && firstProperty.GetInterface(nameof(IEnumerable)) == null)
                {
                    right = Expression.Equal(GetPropertyExpression(null, left, fieldName), Expression.Convert(constant, lastProperty), false, m);

                }
                else
                {
                    right = GetPropertyExpression(null, left, fieldName, Expression.Convert(constant, lastProperty));
                }
                expression = Expression.Or(right, expression);
            }
            return expression;
        }


        #endregion

        #region 忽略掉一些常见的不用的key
        /// <summary>
        /// 忽略掉一些常见的不用的key
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        private bool Ignore(string key)
        {
            if (new[] { "draw", "length", "start" }.Contains(key))
            {
                return true;
            }
            if (key.StartsWith("columns[") || key.StartsWith("search["))
            {
                return true;
            }
            return false;
        }
        #endregion

        #region 查找属性，支持导航属性
        /// <summary>
        /// 查找属性，支持导航属性
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="entity"></param>
        /// <returns></returns>
        private static PropertyInfo FindProperty(string fieldName, Type entity)
        {
            PropertyInfo property = null;
            var fieldNames = fieldName.Split('.');
            for (var i = 0; i < fieldNames.Length; i++)
            {
                property = entity.GetProperty(fieldNames[i], flag);
                //break;

                //不需要递归返回导航属性了。
                if (i + 1 < fieldNames.Length)
                {
                    Type outputType;
                    if (!property.PropertyType.GenericTypeArguments.Any())
                    {
                        outputType = property.PropertyType;
                    }
                    else
                    {
                        outputType = property.PropertyType.GenericTypeArguments.First();
                    }

                    property = FindProperty(fieldNames[++i], outputType);
                    if (property == null)
                    {
                        return null;
                    }
                }
            }

            return property;
        }
        #endregion

        #region 获取属性的表达式，支持导航属性

        /// <summary>
        /// 获取属性的表达式，支持导航属性
        /// </summary>
        /// <param name="source"></param>
        /// <param name="param"></param>
        /// <param name="fieldName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private Expression GetPropertyExpression(Expression source, ParameterExpression param, string fieldName, Expression value = null)
        {
            string[] fields = fieldName.Split('.');
            if (source == null)
            {
                var fistProperty = typeof(TEntity).GetProperty(fields.First(), flag);

                //属性本身是集合
                //m.Roles.Any(y => (y.Id == Convert(1)))
                if (fistProperty.PropertyType != typeof(string) && fistProperty.PropertyType.GetInterface(nameof(IEnumerable)) != null)
                {
                    PropertyInfo[] tree = {
                        fistProperty,
                        FindProperty(fieldName,typeof(TEntity))
                    };
                    Type[] treeType =
                    {
                        tree[0].PropertyType.GenericTypeArguments[0],
                        tree[1].PropertyType
                    };

                    ParameterExpression bParameter = Expression.Parameter(treeType[0], "y");

                    var idProperty = Expression.Property(bParameter, tree[1]);
                    var compare = Expression.Equal(idProperty, value);


                    var compareExpression = Expression.Lambda(compare, bParameter);

                    // a => a.Roles.Any(compareExpression)
                    var foosProperty = Expression.Property(param, tree[0]);
                    MethodInfo method = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2).MakeGenericMethod(treeType[0]);

                    var anyMethod = Expression.Call(method, foosProperty, compareExpression);


                    return anyMethod;
                }


                source = Expression.Property(param, typeof(TEntity).GetProperty(fields.First(), flag));
            }
            else
            {
                source = Expression.Property(source, fields.First());
            }
            foreach (var item in fields.Skip(1))
            {
                //递归出表达式
                source = GetPropertyExpression(source, param, item);
            }
            return source;
        }

        #endregion




    }

    public static class TypeExtensions
    {
        private class SimpleTypeComparer : IEqualityComparer<Type>
        {
            public bool Equals(Type x, Type y)
            {
                return x.Assembly == y.Assembly &&
                       x.Namespace == y.Namespace &&
                       x.Name == y.Name;
            }

            public int GetHashCode(Type obj)
            {
                throw new NotImplementedException();
            }
        }

        public static MethodInfo GetGenericMethod(this Type type, string name, Type[] parameterTypes)
        {
            var methods = type.GetMethods();
            foreach (var method in methods.Where(m => m.Name == name))
            {
                var methodParameterTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();

                if (methodParameterTypes.SequenceEqual(parameterTypes, new SimpleTypeComparer()))
                {
                    return method;
                }
            }

            return null;
        }
    }
}
